﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Simple_search.Data
{
    public class SearchResult
    {
        public SearchResult(IBook book, int start, int length) {
            Book = book;
            Start = start;
            End = start + length;
            Length = length;
            StartBlockId = book.GetBlockId(Start);
            EndBlockId = book.GetBlockId(End);
        }
        public IBook Book { get; }
        public int Start { get; }
        public int End { get; }
        public int StartBlockId { get; }
        public int EndBlockId { get; }
        public int Length { get; }

        public static List<SearchResult> Merge(List<SearchResult> results)
        {
            List<SearchResult> mergedResults = new List<SearchResult>();
            if (results.Count == 0)
            {
                return mergedResults;
            }

            IOrderedEnumerable<SearchResult> ordered = results.OrderBy(result => result.Start);

            int endPointer = 0;
            SearchResult? lastResult = null;
            List<SearchResult> resultHeap = new List<SearchResult>();

            foreach (SearchResult result in ordered)
            {
                if (lastResult is null)
                {
                    lastResult = result;
                    endPointer = result.End;
                } else
                {
                    if (result.Book != lastResult.Book)
                    {
                        throw new Exception("Can't merge results from different book!");
                    }
                    if (result.Start <= endPointer)
                    {
                        resultHeap.Add(lastResult);
                        lastResult = result;
                        endPointer = endPointer < result.End ? result.End : endPointer;
                    }
                    else
                    {
                        if (resultHeap.Count > 0)
                        {
                            resultHeap.Add(lastResult);
                            mergedResults.Add(SearchResult.MergeResult(resultHeap, endPointer));
                            resultHeap.Clear();
                        } else
                        {
                            mergedResults.Add(lastResult);
                        }
                        lastResult = result;
                        endPointer = result.End;
                    }
                }
            }

            if (resultHeap.Count > 0)
            {
                resultHeap.Add(lastResult);
                mergedResults.Add(SearchResult.MergeResult(resultHeap, endPointer));
            }
            else
            {
                mergedResults.Add(lastResult);
            }
            return mergedResults;
        }

        private static SearchResult MergeResult(List<SearchResult> results, int end)
        {
            IBook book = results[0].Book;
            int start = results[0].Start;
            int length = end - start;
            return new SearchResult(book, start, length); 
        }
    }
}
